#!/usr/bin/env python
"""
Celery服务
整合调度器和执行器功能，使用Celery进行任务队列管理
"""
import sys
import os
from flask import Flask, Blueprint, jsonify, request
from datetime import datetime

# 添加项目根目录到路径
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))

from config.config import get_config
from services.shared.database import Task, TaskExecution
from services.shared.models import TaskStatus, TaskType
from services.shared.logger import get_logger
from services.shared.celery_app import celery_app
from services.shared.db_pool import db_pool, get_db_session, close_db_session
from services.scheduler.tasks import schedule_task, reload_tasks
from services.executor.tasks import execute_task
from services.executor.dependency_api import dependency_bp
from services.shared.utils import get_next_run_time

# 获取配置
config = get_config()

# 获取日志记录器
logger = get_logger('celery_service')

# 创建蓝图
api_bp = Blueprint('api', __name__)


@api_bp.route('/tasks', methods=['GET'])
def get_tasks():
    """获取所有任务"""
    session = get_db_session()
    try:
        tasks = session.query(Task).all()
        task_dicts = [task.to_dict() for task in tasks]
        
        # 实时计算每个任务的下次执行时间
        logger.debug(f"开始实时计算所有任务的下次执行时间，任务数量: {len(task_dicts)}")
        for task_dict in task_dicts:
            task_item = next((t for t in tasks if t.id == task_dict['id']), None)
            if task_item and task_item.cron_expression:
                logger.debug(f"计算任务 ID: {task_item.id}, Cron表达式: {task_item.cron_expression}")
                next_run = get_next_run_time(task_item.cron_expression)
                if next_run:
                    logger.debug(f"任务 {task_item.id} 的下次执行时间: {next_run}")
                    task_dict['next_run_at'] = next_run.isoformat()
                else:
                    logger.warning(f"任务 {task_item.id} 的下次执行时间计算失败，Cron表达式: {task_item.cron_expression}")
                    task_dict['next_run_at'] = None
            else:
                if not task_item:
                    logger.warning(f"未找到任务项，ID: {task_dict['id']}")
                elif not task_item.cron_expression:
                    logger.debug(f"任务 {task_item.id} 没有Cron表达式，跳过计算下次执行时间")
        logger.debug(f"实时计算所有任务的下次执行时间完成")
        
        return jsonify({
            'success': True,
            'tasks': task_dicts
        })
    finally:
        session.close()


@api_bp.route('/tasks', methods=['POST'])
def add_task():
    """添加任务"""
    session = get_db_session()
    try:
        data = request.json
        
        # 创建任务
        task = Task(
            name=data.get('name'),
            description=data.get('description', ''),
            type=data.get('type'),
            script_path=data.get('script_path', ''),
            command=data.get('command', ''),
            cron_expression=data.get('cron_expression', ''),
            timeout=data.get('timeout', config.TASK_TIMEOUT),
            max_retries=data.get('max_retries', 0),
            parameters=data.get('parameters', {}),
            status=TaskStatus.ACTIVE if data.get('active', True) else TaskStatus.INACTIVE,
            created_by=data.get('created_by', 'system'),
            created_at=datetime.now(),
            updated_at=datetime.now()
        )
        
        session.add(task)
        session.commit()
        
        # 如果是定时任务，重新加载调度器
        if task.type == TaskType.SCHEDULED and task.status == TaskStatus.ACTIVE:
            reload_tasks.delay()
        
        return jsonify({
            'success': True,
            'message': '任务添加成功',
            'task_id': task.id
        })
    except Exception as e:
        logger.error(f"添加任务失败: {str(e)}")
        return jsonify({
            'success': False,
            'error': f"添加任务失败: {str(e)}"
        }), 400
    finally:
        session.close()


@api_bp.route('/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
    """更新任务"""
    session = get_db_session()
    try:
        task = session.query(Task).get(task_id)
        if not task:
            return jsonify({
                'success': False,
                'error': f"任务不存在: {task_id}"
            }), 404
        
        data = request.json
        
        # 更新任务属性
        if 'name' in data:
            task.name = data['name']
        if 'description' in data:
            task.description = data['description']
        if 'script_path' in data:
            task.script_path = data['script_path']
        if 'command' in data:
            task.command = data['command']
        if 'cron_expression' in data:
            task.cron_expression = data['cron_expression']
        if 'timeout' in data:
            task.timeout = data['timeout']
        if 'max_retries' in data:
            task.max_retries = data['max_retries']
        if 'parameters' in data:
            task.parameters = data['parameters']
        if 'active' in data:
            task.status = TaskStatus.ACTIVE if data['active'] else TaskStatus.INACTIVE
        
        task.updated_at = datetime.now()
        session.commit()
        
        # 如果是定时任务，重新加载调度器
        if task.type == TaskType.SCHEDULED:
            reload_tasks.delay()
        
        return jsonify({
            'success': True,
            'message': '任务更新成功'
        })
    except Exception as e:
        logger.error(f"更新任务失败: {str(e)}")
        return jsonify({
            'success': False,
            'error': f"更新任务失败: {str(e)}"
        }), 400
    finally:
        session.close()


@api_bp.route('/tasks/<int:task_id>', methods=['DELETE'])
def remove_task(task_id):
    """删除任务"""
    session = get_db_session()
    try:
        task = session.query(Task).get(task_id)
        if not task:
            return jsonify({
                'success': False,
                'error': f"任务不存在: {task_id}"
            }), 404
        
        # 删除任务
        session.delete(task)
        session.commit()
        
        # 如果是定时任务，重新加载调度器
        if task.type == TaskType.SCHEDULED:
            reload_tasks.delay()
        
        return jsonify({
            'success': True,
            'message': '任务删除成功'
        })
    except Exception as e:
        logger.error(f"删除任务失败: {str(e)}")
        return jsonify({
            'success': False,
            'error': f"删除任务失败: {str(e)}"
        }), 400
    finally:
        session.close()


@api_bp.route('/tasks/<int:task_id>/run', methods=['POST'])
def run_task_api(task_id):
    """立即运行任务"""
    session = get_db_session()
    try:
        task = session.query(Task).get(task_id)
        if not task:
            return jsonify({
                'success': False,
                'error': f"任务不存在: {task_id}"
            }), 404
        
        # 获取任务参数
        params = request.json.get('parameters', {})
        
        # 调度任务执行，明确指定队列为executor
        result = schedule_task.apply_async(
            args=[task_id, params],
            queue='executor'  # 明确指定使用executor队列
        )
        
        return jsonify({
            'success': True,
            'message': '任务已调度执行',
            'task_id': task_id,
            'celery_task_id': result.id
        })
    except Exception as e:
        logger.error(f"运行任务失败: {str(e)}")
        return jsonify({
            'success': False,
            'error': f"运行任务失败: {str(e)}"
        }), 400
    finally:
        session.close()


@api_bp.route('/tasks/<int:task_id>/enable', methods=['POST'])
def enable_task(task_id):
    """启用任务"""
    session = get_db_session()
    try:
        task = session.query(Task).get(task_id)
        if not task:
            return jsonify({
                'success': False,
                'error': f"任务不存在: {task_id}"
            }), 404
        
        # 更新任务状态
        task.status = TaskStatus.ACTIVE
        task.updated_at = datetime.now()
        session.commit()
        
        # 如果是定时任务，重新加载调度器
        if task.type == TaskType.SCHEDULED:
            reload_tasks.delay()
        
        return jsonify({
            'success': True,
            'message': '任务已启用'
        })
    except Exception as e:
        logger.error(f"启用任务失败: {str(e)}")
        return jsonify({
            'success': False,
            'error': f"启用任务失败: {str(e)}"
        }), 400
    finally:
        session.close()


@api_bp.route('/tasks/<int:task_id>/disable', methods=['POST'])
def disable_task(task_id):
    """禁用任务"""
    session = get_db_session()
    try:
        task = session.query(Task).get(task_id)
        if not task:
            return jsonify({
                'success': False,
                'error': f"任务不存在: {task_id}"
            }), 404
        
        # 更新任务状态
        task.status = TaskStatus.INACTIVE
        task.updated_at = datetime.now()
        session.commit()
        
        # 如果是定时任务，重新加载调度器
        if task.type == TaskType.SCHEDULED:
            reload_tasks.delay()
        
        return jsonify({
            'success': True,
            'message': '任务已禁用'
        })
    except Exception as e:
        logger.error(f"禁用任务失败: {str(e)}")
        return jsonify({
            'success': False,
            'error': f"禁用任务失败: {str(e)}"
        }), 400
    finally:
        session.close()


@api_bp.route('/executions', methods=['GET'])
def get_executions():
    """获取任务执行记录"""
    task_id = request.args.get('task_id')
    limit = int(request.args.get('limit', 100))
    
    session = get_db_session()
    try:
        query = session.query(TaskExecution)
        if task_id:
            query = query.filter_by(task_id=task_id)
        
        executions = query.order_by(TaskExecution.started_at.desc()).limit(limit).all()
        
        return jsonify({
            'success': True,
            'executions': [execution.to_dict() for execution in executions]
        })
    finally:
        session.close()


@api_bp.route('/executions/<string:execution_id>', methods=['GET'])
def get_execution(execution_id):
    """获取任务执行详情"""
    session = get_db_session()
    try:
        execution = session.query(TaskExecution).filter_by(execution_id=execution_id).first()
        if not execution:
            return jsonify({
                'success': False,
                'error': f"执行记录不存在: {execution_id}"
            }), 404
        
        return jsonify({
            'success': True,
            'execution': execution.to_dict()
        })
    finally:
        session.close()


@api_bp.route('/health', methods=['GET'])
def health_check():
    """健康检查"""
    # 直接返回健康状态，不执行任何数据库操作
    try:
        # 检查Celery是否可用 - 使用ping方法更准确
        try:
            ping_result = celery_app.control.ping(timeout=1.0)
            celery_status = 'healthy' if ping_result else 'unhealthy'
        except Exception:
            celery_status = 'unhealthy'
        
        # 不执行任何数据库查询，直接返回健康状态
        return jsonify({
            'status': 'healthy',
            'service': 'celery-service',
            'celery': celery_status,
            'timestamp': datetime.now().isoformat()
        })
    except Exception as e:
        logger.error(f"健康检查失败: {str(e)}")
        return jsonify({
            'status': 'unhealthy',
            'error': str(e),
            'timestamp': datetime.now().isoformat()
        }), 500


def create_app():
    """创建Flask应用"""
    app = Flask(__name__)
    
    # 加载配置
    app.config.from_object(config)
    
    # 初始化WebSocket服务URL
    from services.shared.websocket_service import get_websocket_service
    websocket_service = get_websocket_service()
    
    # 在Docker环境中使用容器名，否则使用localhost
    if os.environ.get('SERVICE_TYPE') == 'celery-workers':
        web_service_url = f"http://xbyjob-app-services:{config.WEB_SERVICE_PORT}"
    else:
        web_service_url = f"http://localhost:{config.WEB_SERVICE_PORT}"
    
    websocket_service.set_web_service_url(web_service_url)
    logger.info(f"WebSocket服务URL设置为: {web_service_url}")
    
    # 注册蓝图
    app.register_blueprint(api_bp, url_prefix='/api')
    app.register_blueprint(dependency_bp, url_prefix='/api/dependencies')
    
    # 启动时任务状态检查 - 已禁用，防止将正常任务标记为失败后又触发新任务
    # def delayed_startup_check():
    #     """延迟执行启动检查"""
    #     import time
    #     time.sleep(5)  # 等待5秒确保服务完全启动
    #     try:
    #         from services.shared.startup_task_checker import run_startup_task_check
    #         logger.info("开始执行启动时任务状态检查...")
    #         run_startup_task_check()
    #         logger.info("启动时任务状态检查完成")
    #     except Exception as e:
    #         logger.error(f"启动时任务状态检查失败: {e}")
    # 
    # # 在后台线程中执行启动检查
    # import threading
    # startup_thread = threading.Thread(target=delayed_startup_check, daemon=True)
    # startup_thread.start()
    
    # 注册请求钩子，管理数据库会话
    @app.before_request
    def before_request():
        # 不需要显式创建会话，get_db_session会自动处理
        pass
    
    @app.teardown_request
    def teardown_request(exception=None):
        # 关闭数据库会话
        close_db_session()
    
    return app




if __name__ == '__main__':
    # 创建Flask应用
    app = create_app()
    # 使用配置文件中的端口设置
    port = int(os.environ.get('SERVICE_PORT', config.SERVICE_PORT))
    app.run(host='0.0.0.0', port=port, debug=config.DEBUG)